package interfaz2;

import java.io.FileNotFoundException;

import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TreeItem;

import core.Post;
import dustconf.DustConf;
import dustlog.LogController;
import events.PostStateChangedEvent;
import events.PostStateChangedListener;

/**
 * Following the MVC design pattern, this class is the controller of the Posts.
 * It is a bit tricky, since the TreeItem cannot be subclassed, this class has
 * 3 main attributes:
 * 	- post: The model in the MVC design pattern. The Post instance this
 * 			controller is controlling.
 * 	- view: The view in the MVC design pattern.
 *	- item: A TreeItem where this controller is attached to. Any SWT widget can
 *			have data attached to it, PostControllers are attached to
 *			TreeItems, that TreeItem will pass the events to the controller. 
 * 
 * @author ole
 */
public class PostController {
	// Class constants.
	private static final int STYLE_BOLD = SWT.BOLD;
	private static final int STYLE_NORMAL = SWT.NORMAL;
	private static final int STYLE_DEFAULT = STYLE_NORMAL;
	
	// Class attributes.
	private Post post;
	private TreeItem item;
	private PostView view;
	
	/*
	 * Private classes.
	 */
	
	/**
	 * This class handles when a Post change its state from actual to archived
	 * so we can change the left icon.
	 * 
	 * @author ole
	 */
	private class ChangePostIcon
	implements PostStateChangedListener, Runnable {
		private PostStateChangedEvent event;
		
		@Override
		public void handleEvent(PostStateChangedEvent event) {
			this.event = event;
			if (!item.isDisposed())
				item.getDisplay().asyncExec(this);
		}

		@Override
		public void run() {
			Image img;
			String img_str;
			
			if (event.getActualState() == Post.Post_state.ARCHIVED)
				img_str = DustConf.getImagesFolder() + "/hdd.png";
			else
				img_str = DustConf.getImagesFolder() + "/rss.png";
				
			try {
				img = IfaceUtils.buildImage(img_str);
				item.setImage(img);
			}
			catch (FileNotFoundException e) {
				System.err.println("[WARN]: File " + e.getMessage() +
								   " not found.");
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * Class that will handle the selection of the "Delete post" option in the
	 * menu that pops up when right click is made over a post.
	 */
	private class DeletePostListener extends SelectionAdapter {
		/**
		 * Delete the Post.
		 */
		@Override
		public void widgetSelected(SelectionEvent event) {
			TreeItem blog_item;
			BlogController blog_ctrl;
			
			// Delete the Post from the Blog it belongs.
			blog_item = item.getParentItem();
			if (blog_item.getData() instanceof BlogController) {
				blog_ctrl = (BlogController)blog_item.getData();
				
				// For some reason we couldn't remove the post.
				if (!blog_ctrl.getBlog().removePost(post)) {
					System.err.println("[WARN]: Couldn't remove post " +
									   post.getTitle());
					return;
				}
			}
			
			// Delete the Post from the tree.
			item.dispose();
			
			// Delete the view of this post.
			view.dispose();
			
			// Log the action.
			LogController.log("Deleted post " + post.getTitle());
		}
	}
	
	/*
	 * Private methods.
	 */
	
	/**
	 * This method returns the index inside the TreeItem passed, where Post
	 * should be added in order to has chronological order among the children
	 * of that TreeItem. 
	 */
	private int guessPositionInTree(TreeItem node, Post post) {
		Post child_post;
		
		// Get every child and compare the date of its Post associated with
		// this Post date. Here we are assuming that the children have a
		// chronological order so when we found an older Post we have found
		// our position.
		for (TreeItem child: node.getItems()) {
			if (child.getData() instanceof PostController) {
				child_post = ((PostController)child.getData()).getPost();
				
				/** TODO: Yes, we can refactor these two ifs in just one, but
				 * since the next if is an special case I prefer have them
				 * splitted up to document them separately. **/
				// What about if the child_post or even post has no date?.
				// Pfff, consider post newer than child_post...
				if (post.getDate() == null || child_post.getDate() == null)
					return node.indexOf(child);
			
				// If this Post is newer than the child Post at this position
				// it should be added at this index (every older Post will be
				// shifted one position, but we need to still comparing.
				if (post.getDate().after(child_post.getDate()))
					return node.indexOf(child);
			}
		}
		
		// Reaching this code means that every Post in that node is newer than
		// this Post, so its position should be the tail of the list of
		// children.
		return node.getItems().length;
	}
	
	/**
	 * Sets the style of the letters of this post. Used mainly to change the
	 * style between an already read post and a not read one.
	 * 
	 * @param style
	 * 		One of the constants SWT.<style> allowed for the class FontData.
	 * 		The constants defined at the top of the class are intended to be
	 * 		used here.
	 */
	private void setStyle(int style) {
		Font font;
		FontData font_data;
		
		font = item.getFont();
		font_data = font.getFontData()[0];
		font_data.setStyle(style);
		font = new Font(item.getDisplay(), font_data);
		item.setFont(font);
	}
	
	/**
	 * Class constructor. This constructor will add the TreeItem into the
	 * Tree before other TreeItems which Posts date are before or equal to
	 * the date of this Post. 
	 * 
	 * @param post:
	 * 		The post associated to this view.
	 * @param blog_node:
	 * 		The controller will create a TreeItem where it will be accessed to,
	 * 		blog_node is its parent where this new TreeItem will depends on.
	 * @param browser:
	 * 		The browser where the post must be shown.
	 * @param postview_comp:
	 * 		The Composite where the PostView will be shown.
	 * @throws FileNotFoundException 
	 */
	public PostController(Post post, TreeItem blog_node,
						  Browser browser, DustComposite view_comp) {
		MultiViewComposite mvc;
		GridData grid;
		Image img;
		int index;
		
		// Guess where have we to add the new item in the tree. It will
		// be added before every Post older or equal.
		index = guessPositionInTree(blog_node, post);
		
		// Create the item in the tree.
		item = new TreeItem(blog_node, STYLE_DEFAULT, index);
		item.setText(post.getTitle());
		item.setData(this);
		try {
			if (post.isActual())
				img = IfaceUtils.buildImage(DustConf.getImagesFolder() +
											"/rss.png");
			else
				img = IfaceUtils.buildImage(DustConf.getImagesFolder() +
											"/hdd.png");
			item.setImage(img);
		}
		catch (FileNotFoundException e) {
			System.err.println("[WARN]: File " + e.getMessage() + " not found.");
			e.printStackTrace();
		}
		
		if (!post.hasBeenRead())
			setReadStyle(false);
		
		// The Post we are maintaining. Add a handler to change the left icon
		// when needed.
		this.post = post;
		this.post.addPostStateChangedListener(new ChangePostIcon());
		
		// Create the view.
		view = new PostView(view_comp, post, browser);
		if (view_comp instanceof MultiViewComposite) {
			mvc = (MultiViewComposite)view_comp;
			view.addDisposeListener(mvc.new ChildDisposeListener());
		}
		grid = new GridData(SWT.FILL, SWT.FILL, true, false);
		view.setLayoutData(grid);
		view.hide();
	}
	
	/**
	 * Class constructor.
	 * 
	 * @param post:
	 * 		The post associated to this view.
	 * @param blog_node:
	 * 		The controller will create a TreeItem where it will be accessed to,
	 * 		blog_node is its parent where this new TreeItem will depends on.
	 * @param browser:
	 * 		The browser where the post must be shown.
	 * @param postview_comp:
	 * 		The Composite where the PostView will be shown.
	 * @param index:
	 * 		The position inside the parent TreeItem.
	 */
	public PostController(Post post, TreeItem blog_node,
						  Browser browser, DustComposite view_comp,
						  int index) {
		MultiViewComposite mvc;
		GridData grid;
		Image img;
		
		// Create the item in the tree.
		item = new TreeItem(blog_node, STYLE_DEFAULT, index);
		item.setText(post.getTitle());
		item.setData(this);
		try {
			if (post.isActual())
				img = IfaceUtils.buildImage(DustConf.getImagesFolder() +
											"/rss.png");
			else
				img = IfaceUtils.buildImage(DustConf.getImagesFolder() +
											"/hdd.png");
			item.setImage(img);
		}
		catch (FileNotFoundException e) {
			System.err.println("[WARN]: File " + e.getMessage() + " not found.");
			e.printStackTrace();
		}
		
		if (!post.hasBeenRead())
			setReadStyle(false);
		
		// The Post we are maintaining. Add a handler to change the left icon
		// when needed.
		this.post = post;
		this.post.addPostStateChangedListener(new ChangePostIcon());
		
		// Create the view.
		view = new PostView(view_comp, post, browser);
		if (view_comp instanceof MultiViewComposite) {
			mvc = (MultiViewComposite)view_comp;
			view.addDisposeListener(mvc.new ChildDisposeListener());
		}
		grid = new GridData(SWT.FILL, SWT.FILL, true, false);
		view.setLayoutData(grid);
		view.hide();
	}
	
	/**
	 * Returns the post associated to this PostView.
	 * 
	 * @return:
	 * 		The Post instance associated to this PostView.
	 */
	public Post getPost() {
		return post;
	}
	
	/**
	 * Returns the shell of this PostView.
	 * 
	 * @return:
	 * 		The shell of this BlogView.
	 */
	public Shell getShell() {
		return item.getDisplay().getShells()[0];
	}
	
	/**
	 * Returns the PostView associated to this controller.
	 * 
	 * @return:
	 * 		The PostView this controller is maintaining.
	 */
	public PostView getView() {
		return view;
	}
	
	/**
	 * Set the style of this PostView depending on if it has been read or
	 * not, if true then the text of the TreeItem will be normal but if the
	 * Post hadn't been read the text will be bold.
	 * 
	 * @param readed:
	 * 		A boolean indicating if the Post have been read or not.
	 */
	public void setReadStyle(boolean read) {
		if (read)
			this.setStyle(STYLE_NORMAL);
		else
			this.setStyle(STYLE_BOLD);
	}
	
	/**
	 * Shows a menu with the options about a Post.
	 */
	public void showPostMenu() {
		Menu menu;
		MenuItem deletepost;
		
		// Create the menu, then fill it with the options.
		menu = new Menu(getShell());
		
		// Option to delete this post. If this post is not removable because
		// it is not archived show this option disabled.
		deletepost = new MenuItem(menu, SWT.PUSH);
		deletepost.setText("Delete post");
		deletepost.addSelectionListener(new DeletePostListener());
		if (!post.isArchived())
			deletepost.setEnabled(false);
		
		// Show the menu.
		menu.setVisible(true);
	}
}
